﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.LoadTesting;

namespace CustomLoadProfiles
{
    // This the pattern group class
    // It contains the list of profiles to be simulated in a specified time window
    // Each group can contain several load test profiles
    // During a load run, the test engine will cycle through the LoadTestProfiles and ajust the user load 
    public class LoadProfileGroup
    {
        string m_name;
        List<LoadProfileWrapper> m_profileGroup;
        int m_startTime;
        int m_endTime;
        int m_duration;
        int m_patternCycleDuration;
        int m_timeInGroup;
        int m_previousCycleCount;

        public LoadProfileGroup(string name, int duration)
        {
            m_name = name;
            m_duration = duration;
            m_timeInGroup = 0;
            m_previousCycleCount = 0;
            m_patternCycleDuration = 0;
            m_profileGroup = new List<LoadProfileWrapper>();
        }

        public void AddProfile(LoadProfileWrapper profile)
        {
            m_profileGroup.Add(profile);
            m_patternCycleDuration += profile.Duration;
        }

        // The load profiles to be simulated
        public List<LoadProfileWrapper> LoadProfiles
        { get { return m_profileGroup; } }

        public int StartTime
        {
            get { return m_startTime; }
            set { m_startTime = value; }
        }

        public int EndTime
        {
            get { return m_endTime; }
            set { m_endTime = value; }
        }

        public int Duration
        { get { return m_duration; } }

        public String Name
        { get { return m_name; } }

        // This function will find out what load test profile will be used for current heartbeat event
        // If no change will be made, null will be returned
        public LoadTestLoadProfile GetLoadProfile(int elapsedTime, int currentUserLoad)
        {
            // Calculate the elapased time in the group
            m_timeInGroup = elapsedTime - m_startTime;

            int currentCycleCount = m_timeInGroup / m_patternCycleDuration;

            // Reset the InUse status of each profile to false at the beginning of each cycle
            if (m_previousCycleCount < currentCycleCount && m_timeInGroup % m_patternCycleDuration != 0)
            {
                m_previousCycleCount++;

                for (int i = 0; i < LoadProfiles.Count; i++)
                {
                    LoadProfileWrapper profileWrapper = LoadProfiles[i];
                    profileWrapper.InUse = false;
                }
            }

            // Find out if new load test profile should be applied 
            int durationLength = 0;
            foreach (LoadProfileWrapper profileWrapper in LoadProfiles)
            {
                durationLength += profileWrapper.Duration;

                if ((m_timeInGroup == 0) || // the first profile
                    (m_timeInGroup > 0 && m_timeInGroup % m_patternCycleDuration <= durationLength))
                {
                    if (!profileWrapper.InUse)
                    {
                        // Found the new profile to be applied
                        profileWrapper.UpdateProfileStatus(true, currentUserLoad);
                        return (LoadTestLoadProfile)profileWrapper.Profile;
                    }

                    break;
                }
            }

            return null;
        }
    }

    /// <summary>
    /// Manages all pattern groups to be simulated in the load test run
    /// </summary>
    public class LoadProfileManager
    {
        List<LoadProfileGroup> m_profileGroups;
        private string previousGroup;

        public LoadProfileManager()
        {
            m_profileGroups = new List<LoadProfileGroup>();
        }

        // All the profiles included in this pattern
        public List<LoadProfileGroup> ProfileGroups
        {
            get { return m_profileGroups; }
            set { m_profileGroups = value; }
        }

        // Get the current Load profile based on the elapsed time
        public LoadTestLoadProfile GetCurrentLoadProfile(int elapsedSeconds, int currentUserLoad)
        {
            LoadTestLoadProfile profile = null;
            bool foundProfileGroup = false;

            foreach (LoadProfileGroup group in m_profileGroups)
            {
                // Get the pattern group to be simulated
                if (elapsedSeconds <= group.EndTime && elapsedSeconds > group.StartTime)
                {
                    foundProfileGroup = true;

                    if (!String.Equals(previousGroup, group.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        previousGroup = group.Name;
                    }

                    // Get the profile
                    profile = group.GetLoadProfile(elapsedSeconds, currentUserLoad);
                    break;
                }
            }

            // If no profile group is found, the load test run will continue with a const load

            if (foundProfileGroup == false)
            {
                profile = new LoadTestConstantLoadProfile();
                ((LoadTestConstantLoadProfile)profile).UserCount = currentUserLoad;
            }

            return profile;
        }

        // Set the start/end times for each pattern group
        public void SetStartEndTimeForProfileGroups(int elapsedSeconds)
        {
            int prevGroupEndTime = elapsedSeconds;
            int duration = 0;
            foreach (LoadProfileGroup group in m_profileGroups)
            {
                // set the start time 
                group.StartTime = prevGroupEndTime;

                // set the end time
                duration += group.Duration;
                group.EndTime = duration;

                // update the start time for next pattern group
                prevGroupEndTime = duration;
            }
        }
    }
}

